﻿using System.Collections.Generic;
using System.Linq;
using System.Collections.Concurrent;

namespace SharpBackTestLib.Collections
{
    /// <summary>
    /// This class is intended to manage a ConcurrentQueue using a fixed size, so that when an element is enqueued and the maximum size is reached, the last element is automatically dequeued.
    /// It has a very limited number of methods that will probably be enough for our usage.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class FixedSizeQueue<T>
    {
        private readonly ConcurrentQueue<T> _queue;
        private readonly object _lockObject;

        public int Size
        {
            get;
            private set;
        }

        public FixedSizeQueue(int size)
        {
            Size = size;
            _queue = new ConcurrentQueue<T>();
            _lockObject = new object();
        }

        /// <summary>
        /// The point here is to make sure the queue's size sticks to the limit given in the constructor.
        /// </summary>
        /// <param name="item">The item to enqueue</param>
        public void Enqueue(T item)
        {
            lock (_lockObject)
            {
                while (_queue.Count >= Size)
                {
                    T returnedItem;
                    _queue.TryDequeue(out returnedItem);
                }

                _queue.Enqueue(item);
            }
        }

        /// <summary>
        /// Simply reuses the ConcurrentQueue's TryDequeue() method.
        /// </summary>
        /// <param name="result"></param>
        /// <returns></returns>
        public bool TryDequeue(out T result)
        {
            return _queue.TryDequeue(out result);
        }

        /// <summary>
        /// Thread-safe implementation of the GetEnumerator() method. Any subsequent modification of the internal queue will have no incidence on the returned IEnumerator
        /// </summary>
        /// <returns>An IEnumerator representing the state of the queue at the moment it was requested.</returns>
        public IEnumerator<T> GetEnumerator()
        {
            lock (_lockObject)
            {
                // Copying the items to a list so that other threads enqueuing/dequeuing will not affect the returned IEnumerator.
                List<T> copy = new List<T>(_queue.Count);
                copy.AddRange(_queue);
                return copy.GetEnumerator();
            }
        }

        public int Count
        {
            get
            {
                lock(_lockObject)
                    return _queue.Count();
            }
        }
    }
}
